#include <modules\trajectoryModule.h>
#include <geo\mercatorProjection.h>
#include <geo\osmio.h>
#include <geo\track.h>

trajectoryModule::trajectoryModule(vec2i _tile_res, vec2i _levels_interval, string _gpxsourcefile) : dataModule() {
  tile_res = _tile_res;
  levels_interval = _levels_interval;
  gpxsourcefile = _gpxsourcefile;
}

bool
trajectoryModule::post(mysqlDatabase *db) {

  osmio tracks;
  if (!tracks.open(gpxsourcefile)) return false;

  // check/create texture tables for all levels
  stringstream query;

  bool res = true;
  for (int level = levels_interval[0]; level <= levels_interval[1]; level++) {
    query.str("");
    query << TRAJECTORY_TABLE_BASE_NAME << "_" << level;
    if (!tableExists(db, query.str())) {
      res = res && createTable(db, query.str());
      if (!res) return false;
    }
  }

  for (int level = levels_interval[0]; level <= levels_interval[1]; level++) {

    // prepare trajectories from gpx dataset
    tracks.project(level);
    tracks.refine();
    tracks.encode();
    // end of preparation

    // determine chains for each [col, row] pair and store them in database
    std::vector<track<unsigned char> > etracks = tracks.getEncodedTracks();
    std::vector<track<vec2i> > rtracks = tracks.getRefinedTracks();

    for (unsigned int t = 0; t < etracks.size(); t++) {
      track<unsigned char> etrack = etracks[t];
      track<vec2i> rtrack = rtracks[t];

      for (int c = 0; c < etrack.size(); c++) {
        vec2i ritem = rtrack[c];

        // encode the chain
        unsigned int chain = 0;
        int i = 0;
        for (; i < CHAIN_SIZE; i++) {
          if (c + i >= etrack.size()) break;
          chain += etrack[c + i];
          chain = chain << 3;
        }

        chain += i; // encode chain size

        if (i > 0) {
          chains[ritem.y][ritem.x].push_back(chain);
        }
      }
    }

    map<unsigned int, map<unsigned int, vector<unsigned int> > >::iterator row;
    map<unsigned int, vector<unsigned int> >::iterator col;
    vector<unsigned int>::iterator chainit;

    for (row = chains.begin(); row != chains.end(); row++) {
      int row_id = row->first;
      for (col = row->second.begin(); col != row->second.end(); col++) {
        int col_id = col->first;

        stringstream blob(ios::binary | ios::out | ios::in);
        for (chainit = col->second.begin(); chainit != col->second.end(); chainit++) {
          unsigned int value = *chainit;
          blob.write((const char *) &value, sizeof(unsigned int));
        }

        /*
        // example of read the binary stored chains from blob
        blob.seekg(ios::beg);
        while (!blob.eof()) {
          unsigned int a;
          blob.read((char *) &a, sizeof(unsigned int));
        }
        */

        stringstream query;
        query << "INSERT INTO trajectory_tiles_" << level
              << " (x, y, data) VALUES ("
              << col_id << "," << row_id << "," << "?)";

        try {
          std::auto_ptr<sql::PreparedStatement> prepstmt(db->prepareQuery(query.str()));
          prepstmt->setBlob(1, &blob);
          prepstmt->execute();
          prepstmt->close();
        }
        catch (SQLException &e) {
          cerr << "ERROR: " << e.what();
          cerr << " (MySQL error code: " << e.getErrorCode();
          cerr << ", SQLState: " << e.getSQLState() << ")" << endl;
          exit (0);
        }
      }
    }

    chains.clear();
  }

  return true;
}